//
//  CTVAutoUpdate.m
//  Channltv
//
//  Created by Bruno Keymolen on 10/10/07.
//  Copyright 2007 channl.tv. All rights reserved.
//

// States:
//     ------<-------+--------<--------+----------<---------+-------<---------
//    |              |                 |                    |                 | 
//    v              |                 |                    |                 |
// CTVAUIdle ---> CTVAUCheck     CTVAUInstaller ---> CTVAUAppliance --> CTVAUExecute
//                   ^                 ^ 


#import "CTVAutoUpdate.h"
#import "CTVConfig.h"

#include <sys/types.h>
#include <unistd.h>

@implementation CTVAutoUpdate

+(id)GetInstance {
	static id _CTVSingleton = nil;
	if (_CTVSingleton == nil) {
		_CTVSingleton = [self new];
		[_CTVSingleton setup];
	}	
    return _CTVSingleton;
}


-(void)setup{
	m_state = CTVAUIdle;
	m_versionURLString = [[NSString alloc] initWithFormat:@"http://%@/installers/appletv/1.1/version.txt", [[CTVConfig GetInstance] CTVServer] ];
	m_installerURLString = [[NSString alloc] initWithFormat:@"http://%@/installers/appletv/1.1/installer.sh", [[CTVConfig GetInstance] CTVServer] ];
	m_applianceURLString = [[NSString alloc] initWithFormat:@"http://%@/installers/appletv/1.1/Channltv.frappliance.tar", [[CTVConfig GetInstance] CTVServer] ];

	m_data = [[NSMutableData alloc] init];
	m_listeners = [[NSMutableArray alloc] init];
}


-(void)checkNewVersion{
	if(m_state != CTVAUIdle){
		return;
	}
	
	m_state = CTVAUCheck;
	
	[m_data setData:[NSData dataWithBytes:NULL length:0]];

	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 
	
	//Async download
	NSURL* nurl = [NSURL URLWithString:m_versionURLString];
	NSMutableURLRequest* urlRequest=[NSMutableURLRequest requestWithURL:nurl
							cachePolicy:NSURLRequestReloadIgnoringCacheData
							timeoutInterval:15.0];

	//Modify some Headers
	[urlRequest setValue:[[CTVConfig GetInstance] userAgent] forHTTPHeaderField:@"User-Agent"];

	//Get the data
	NSURLConnection* connection=[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];

	if(connection == nil){
		//Unable to connect, goto IDLE state
		m_state = CTVAUIdle;
		
		[self _informListenersFail];
	}
	
	//The download is ongoing
	
	[pool release];
}



-(void)updateNow{
	if(m_state != CTVAUIdle){
		return;
	}

	m_state = CTVAUInstaller;

	[m_data setData:[NSData dataWithBytes:NULL length:0]];

	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 
	
	//Async download
	NSURL* nurl = [NSURL URLWithString:m_installerURLString];
	NSMutableURLRequest* urlRequest=[NSMutableURLRequest requestWithURL:nurl
							cachePolicy:NSURLRequestReloadIgnoringCacheData
							timeoutInterval:15.0];

	//Modify some Headers
	[urlRequest setValue:[[CTVConfig GetInstance] userAgent] forHTTPHeaderField:@"User-Agent"];

	//Get the data
	NSURLConnection* connection=[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];

	if(connection == nil){
		//Unable to connect, goto IDLE state
		m_state = CTVAUIdle;
		//Infor the listeners
		[self _informListenersFail];
	}
	
	//The download is ongoing
	
	[pool release];
}




/*
 *
 * NSURLConnection Delegates
 *
 */
- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
}


- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
}



/* FAIL */
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
	[connection release];
	[m_data setData:[NSData dataWithBytes:NULL length:0]];
	m_state = CTVAUIdle;
	//Inform the listeners
	[self _informListenersFail];
}


- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
		
	//Write data to memory
	[m_data appendBytes:[data bytes] length:[data length]];
		
}



// Full header is available
// In rare cases, for example in the case of an HTTP load where the content type of the load data is 
// multipart/x-mixed-replace, the delegate will receive more than one connection:didReceiveResponse: message. 
// In the event this occurs, delegates should discard all data previously delivered by 
// connection:didReceiveData:, and should be prepared to handle the, potentially different,
// MIME type reported by the newly reported URL response.
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
	NSHTTPURLResponse* urlResponse = (NSHTTPURLResponse*)response;
	
	NSLog(@"Full header is received");

	if([urlResponse statusCode] >= 400){
		NSLog(@"Full header is received error:%d", [urlResponse statusCode]);

		//Could not download resource
		[connection cancel];
		[m_data setData:[NSData dataWithBytes:NULL length:0]];

		[connection release];
	
		m_state = CTVAUIdle;

		//Inform the listeners
		[self _informListenersFail];

		//Done
	}
}



-(NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse{
	//Return Value
	//The actual cached response to store in the cache. The delegate may return cachedResponse unmodified, 
	//return a modified cached response, or return nil if no cached response should be stored for the 
	//connection.
	return nil;
}


-(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse{
	//Oops, we have a redirect, modify the headers to our needs
	NSMutableURLRequest* urlRequest= [request mutableCopyWithZone:nil];	
		
	//Modify some Headers
	[urlRequest setValue:[[CTVConfig GetInstance] userAgent] forHTTPHeaderField:@"User-Agent"];	
	
	return urlRequest;
}


-(BOOL)updateAvailable{
	return m_updateAvailable;
}




-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
	
	//Done
	[connection release];
	
			
	//Check for update 
	if(m_state == CTVAUCheck){
		m_state = CTVAUDone;
		if( [m_data length] > 0 ){
			char* buf = (char*)[m_data bytes];
			buf[[m_data length]] = 0;
			char* pv = strstr(buf, "version");
			char* pma = strstr(pv   , ":");
			pma++;
			char* pmi = strstr(pma, ".");
			pmi++;
			char* pre = strstr(pmi, ".");
			pre++;

			m_versionMajor = atoi(pma);
			m_versionMinor = atoi(pmi);
			m_versionRevision = atoi(pre);
			
			if(m_versionMajor != [[CTVConfig GetInstance] versionMajor] ||
			   m_versionMinor != [[CTVConfig GetInstance] versionMinor] || 
			   m_versionRevision != [[CTVConfig GetInstance] versionRevision]){
					m_updateAvailable = TRUE;
			}else{
					m_updateAvailable = FALSE;
			}
			
		}

		//Inform our listeners
		[self _informListenersDone];
		
		m_state = CTVAUIdle;
	}
	


	//Update in progress

	if(m_state == CTVAUInstaller){ 
		if( [m_data length] > 0 ){
			NSString* fileName = [NSString stringWithFormat:@"%@installer.sh",[[CTVConfig GetInstance] autoUpdateDir] ];
			[m_data writeToFile:fileName atomically:YES];
		} 		
		//Start downloading the appliance now
		[self _applicanceDownload];
	}else if(m_state == CTVAUAppliance){
		if( [m_data length] > 0 ){
			NSString* fileName = [NSString stringWithFormat:@"%@Channltv.frappliance.tar",[[CTVConfig GetInstance] autoUpdateDir] ];
			[m_data writeToFile:fileName atomically:YES];
		} 		
		//Download ready, execute the installer
		int pid = getpid(); 
		
		NSString* appliancePath =  [[NSBundle bundleForClass:[self class]] bundlePath];
		NSString* tempDir = [[CTVConfig GetInstance] autoUpdateDir];
				
		
		{
			//installer.sh:
			//#$1: /Users/frontrow/channl.tv/AutoUpdate/ 
			//#$2: /System/Library/CoreServices/Finder.app/Contents/PlugIns/Channltv.frappliance 
			//#$3: 157
			NSString* pw = @"frontrow";
			NSString* cmd = [NSString stringWithFormat:@"echo \"%@\" | sudo -S chmod 755 %@installer.sh;echo \"%@\" | sudo -S %@installer.sh %@ %@ %d", pw, tempDir, pw, tempDir, tempDir, appliancePath, pid];
			NSLog(cmd);
			system( [cmd UTF8String] );
		}
		
		
		m_state = CTVAUIdle;
	
	}
	 
	
	
	//Update in progress
//	switch(m_state){
//		case  CTVAUInstaller:
//			 
//		case  CTVAUAppliance:
//		 //--> CTVAUExecute
//	}	
}

-(void)resetUpdateAvailable{
	m_updateAvailable = FALSE;
}

-(BOOL)isProcessing{
	if(m_state == CTVAUIdle || m_state == CTVAUDone){
		return FALSE;
	}else{
		return TRUE;
	}
}

-(void)addListener:(CTVAutoUpdateListener*)listener{
	[m_listeners addObject:listener];
}


-(void)removeListener:(CTVAutoUpdateListener*)listener{
	[m_listeners removeObject:listener];
}


-(NSString*)latestVersion{
	NSString* version = [NSString stringWithFormat:@"%d.%d.%d", m_versionMajor, m_versionMinor, m_versionRevision];
	return version;
}


-(void)_informListenersDone{
	//Inform our listeners
	NSString* version = [NSString stringWithFormat:@"%d.%d.%d", m_versionMajor, m_versionMinor, m_versionRevision];
	NSEnumerator* en = [m_listeners objectEnumerator];
	CTVAutoUpdateListener* listener;
	while(listener = [en nextObject]){
		[listener autoUpdateCheckDone:version];
	}
}

-(void)_informListenersFail{
	//Inform our listeners
	NSEnumerator* en = [m_listeners objectEnumerator];
	CTVAutoUpdateListener* listener;
	while(listener = [en nextObject]){
		[listener autoUpdateCheckFail];
	}
}


//Private
-(void)_applicanceDownload{
	if(m_state != CTVAUInstaller){
		return;
	}

	m_state = CTVAUAppliance;

	[m_data setData:[NSData dataWithBytes:NULL length:0]];

	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 
	
	//Async download
	NSURL* nurl = [NSURL URLWithString:m_applianceURLString];
	NSMutableURLRequest* urlRequest=[NSMutableURLRequest requestWithURL:nurl
							cachePolicy:NSURLRequestReloadIgnoringCacheData
							timeoutInterval:15.0];

	//Modify some Headers
	[urlRequest setValue:[[CTVConfig GetInstance] userAgent] forHTTPHeaderField:@"User-Agent"];

	//Get the data
	NSURLConnection* connection=[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];

	if(connection == nil){
		//Unable to connect, goto IDLE state
		m_state = CTVAUIdle;
		//Infor the listeners
		[self _informListenersFail];
	}
	
	//The download is ongoing
	
	[pool release];
}

@end
